#include "headerdelegate.h"
#include "thumbnailpainter.h"
#include "matrix.h"

#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QThread>
#include <QMouseEvent>

class HeaderDelegatePrivate
{
public:
    HeaderDelegatePrivate(Qt::Orientation ori, HeaderDelegate* q);

    void createUi();

    int index = -1;
    MatrixBase* matrix = nullptr;
    QLabel* dataLabel;
    QLabel* thumbnailLabel;
    HeaderDelegate* q_ptr;
    Qt::Orientation orientation = Qt::Horizontal;
};
HeaderDelegatePrivate::HeaderDelegatePrivate(Qt::Orientation ori, HeaderDelegate *q) :
    orientation(ori), q_ptr(q)
{
    createUi();
}

void HeaderDelegatePrivate::createUi()
{
    dataLabel = new QLabel(q_ptr);
    dataLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
    thumbnailLabel = new QLabel(q_ptr);

    QBoxLayout* layout = nullptr;
    if (orientation == Qt::Vertical) {
        layout = new QHBoxLayout;
        dataLabel->setMaximumWidth(30);
    } else {
        layout = new QVBoxLayout;
        dataLabel->setMaximumHeight(20);
    }

    layout->setSpacing(0);
    layout->setContentsMargins(2, 2, 2, 2);
    layout->addWidget(dataLabel);
    layout->addWidget(thumbnailLabel);
    q_ptr->setLayout(layout);

    q_ptr->setAttribute(Qt::WA_TransparentForMouseEvents);
}

HeaderDelegate::HeaderDelegate(Qt::Orientation ori, QWidget *parent) : QWidget(parent)
{
    d_ptr = new HeaderDelegatePrivate(ori, this);
    setMouseTracking(true);
}

HeaderDelegate::~HeaderDelegate()
{
    delete d_ptr;
}

void HeaderDelegate::setIndex(int index)
{
    d_ptr->index = index;
}

int HeaderDelegate::index() const
{
    return d_ptr->index;
}

void HeaderDelegate::setMatrix(MatrixBase *matrix)
{
    if (d_ptr->matrix != matrix) {
        if (d_ptr->matrix)
            disconnect(d_ptr->matrix, SIGNAL(valueChanged(int,int,QVariant,QVariant)), this, SLOT(onMatrixValueChanged(int,int,QVariant,QVariant)));

        connect(matrix, SIGNAL(valueChanged(int,int,QVariant,QVariant)), this, SLOT(onMatrixValueChanged(int,int,QVariant,QVariant)));
        d_ptr->matrix = matrix;
        updateThumbnail();
    }
}

Qt::Orientation HeaderDelegate::orientation() const
{
    return d_ptr->orientation;
}

void HeaderDelegate::resizeEvent(QResizeEvent *e)
{
    updateThumbnail();
}

void HeaderDelegate::mousePressEvent(QMouseEvent *e)
{
    if (e->buttons() & Qt::LeftButton) {
        QPoint pos = geometry().topLeft() + e->pos();
        emit clicked(pos);
    }
    QWidget::mousePressEvent(e);
}

void HeaderDelegate::mouseMoveEvent(QMouseEvent *e)
{
    setCursor(QCursor(Qt::ArrowCursor));
    QWidget::mouseMoveEvent(e);
}

QSize HeaderDelegate::sizeHint() const
{
    if (orientation() == Qt::Vertical)
        return QSize(120, 30);
    else
        return QSize(80, 60);
}

QSize HeaderDelegate::minimumSizeHint() const
{
    return QSize(80, 30);
}

void HeaderDelegate::onMatrixValueChanged(int row, int col, const QVariant& oldVal, const QVariant& newVal)
{
    if ((orientation() == Qt::Horizontal && col == (index() - 1)) ||
         orientation() == Qt::Vertical && row == (index() - 1))
            updateThumbnail();
}

void HeaderDelegate::updateThumbnail()
{
    if (!d_ptr->matrix)
        return;

    switch (d_ptr->matrix->type()) {
    case MatrixBase::Int:
        fireupPainterThread<IntMatrix>();
        break;

    case MatrixBase::Float:
        fireupPainterThread<FloatMatrix>();
        break;

    case MatrixBase::Double:
        fireupPainterThread<DoubleMatrix>();
        break;

    case MatrixBase::ComplexFloat:
        fireupPainterThread<ComplexFloatMatrix>();
        break;

    case MatrixBase::ComplexDouble:
        fireupPainterThread<ComplexDoubleMatrix>();
        break;
    }
}

void HeaderDelegate::onThumbnailPainterFinished()
{
    AbstractThumbnailPainter* painter = qobject_cast<AbstractThumbnailPainter*>(sender());
    d_ptr->thumbnailLabel->setPixmap(painter->pixmap());
    painter->deleteLater();
}

template <class MatrixType>
void HeaderDelegate::fireupPainterThread()
{
    MatrixType* matrix = dynamic_cast<MatrixType*>(d_ptr->matrix);
    QVector<MatrixType::Scalar> data;

    // because the table header index starts from 1
    // but the passed param starts from 0, so minus 1
    if (this->orientation() == Qt::Horizontal)
        data = matrix->column(index() - 1);
    else
        data = matrix->row(index() - 1);

    QThread* thread = new QThread();
    ThumbnailPainter<MatrixType::Scalar>* painter = new ThumbnailPainter<MatrixType::Scalar>(data, d_ptr->thumbnailLabel->size(), nullptr);

    if (matrix->type() == MatrixBase::ComplexFloat || matrix->type() == MatrixBase::ComplexDouble)
        painter->setPlotColor(Qt::blue);

    connect(thread, SIGNAL(started()), painter, SLOT(start()));
    connect(painter, SIGNAL(finished()), thread, SLOT(terminate()));
    connect(painter, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(painter, SIGNAL(finished()), this, SLOT(onThumbnailPainterFinished()));
    thread->start();
}


